#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <unordered_map>
#include <queue>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"

using namespace std;

#define BUFFER_SIZE 1024

class UdpServer // udp服务器
{
public:
    UdpServer(uint16_t port = 8080, const string ip = "")
        : _ip(ip), _port(port)
    {
    }

    bool InitServer() // 初始化服务器
    {
        // 1.创建套接字，成功，返回文件描述符fd
        _sock = socket(/*套接字类型：网络套接字*/ AF_INET, /*通信类型：数据报*/ SOCK_DGRAM, 0);
        if (_sock < 0) // 创建套接字失败
        {
            logMessage(FATAL, "创建socket失败:%s\n", strerror(errno));
            exit(2);
        }

        // 2.绑定用户设置的ip和port到套接字sock上
        struct sockaddr_in local;     // 绑定套接字需要传入的类型
        bzero(&local, sizeof(local)); // 清零
        local.sin_family = AF_INET;   // 网络套接字

        // 需要告诉对方自己的ip和端口号
        // local.sin_addr.s_addr = inet_addr(_ip.c_str());
        local.sin_addr.s_addr = INADDR_ANY;
        // ip:  1.字符串ip转成uint32_t的4字节整数ip;  2.4字节整数ip转网络字节序;  3.inet_addr同时做了这两件事
        local.sin_port = htons(_port);
        // port:是需要发送到网络的，需要转网络字节序

        if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            // 绑定失败
            logMessage(FATAL, "绑定socket失败:%s\n", strerror(errno));
            exit(3);
        }

        logMessage(WARNING, "初始化udp服务器成功,sock:%d", _sock);

        return true;
    }

    void Start()
    {
        // 作为一个网络服务器是不间断运行，不会退出的
        // 常驻进程，需要非常注意内存使用，防止内存泄漏

        char buffer[BUFFER_SIZE]; // 从网络读到的数据放到此缓冲区

        while (true)
        {
            struct sockaddr_in peer; // 获取发送方的信息：ip，port
            bzero(&peer, sizeof(peer));
            socklen_t len = sizeof(peer); // 输入输出型参数：传入peer大小，获取实际读到的peer大小

            char result[256];
            string cmd_echo; // v2:读取到的命令，子进程执行后通过fp返回的执行结果

            char key[64]; // v1:用户信息键值对：ip-port

            // 1.读取消息
            ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            // 阻塞方式，从套接字读取缓冲区大小的数据，获取发送方信息，返回实际读取字节数
            if (s > 0)
            {
                buffer[s] = 0; // 读取到数据，当作字符串处理

                // 2.获取对端信息
                uint16_t cli_port = ntohs(peer.sin_port); // 客户端口号：网络字节序转主机字节序
                string cli_ip = inet_ntoa(peer.sin_addr); // 4字节网络字节序转4字节主机字节序，再转点分字符串

                // 3.显示信息 or 分析处理信息  &&  回显处理信息

                // v1:实现网络聊天室：所有聊天客户端会同步聊天结果
                snprintf(key, sizeof(key), "%s-%u", cli_ip.c_str(), cli_port);
                auto iter = user_list.find(key); // 查找是否有该用户，没有就添加进用户列表
                if (iter == user_list.end())
                {
                    // 添加用户
                    user_list[key] = peer;
                }

                if (strcmp(buffer, "history") == 0)
                { // 有用户想同步聊天记录
                    for (auto &msg : message_list)
                    {
                        sendto(_sock, msg.c_str(), msg.size(), 0, (struct sockaddr *)&peer, sizeof(peer)); // 同步信息列表中所有记录给这个用户
                    }
                    continue;
                }

                char msg[BUFFER_SIZE];                                                     // 发送缓冲区
                snprintf(msg, sizeof(msg) - 1, "[%lu][%s]:%s\n", time(NULL), key, buffer); // 定义聊天记录
                message_list.push_back(msg);                                               // 保存聊天记录

                // 同步新记录给所有用户
                for (auto iter : user_list)
                {
                    struct sockaddr_in user = iter.second; // 用户信息：ip，port
                    sendto(_sock, msg, sizeof(msg) - 1, 0, (struct sockaddr *)&user, sizeof(user));
                }

                // v2:把传来的信息当作命令处理,实现远程命令控制
                // if (strcasestr(buffer, "rm") != nullptr || strcasestr(buffer, "rmdir") != nullptr)
                // { // 防止客户端恶意命令
                //     string err_msg = "SHIT";
                //     cout << "bad command:" << buffer << endl;
                //     sendto(_sock, err_msg.c_str(), err_msg.size(), 0, (struct sockaddr *)&peer, len);
                //     continue;
                // }

                // FILE *fp = popen(buffer, "r"); // 会自动打开管道pipe,创建子进程fork,并程序替换exec去执行buffer中的命令
                // if (fp == nullptr)
                // {
                //     logMessage(ERROR, "popen: %d:%s", errno, strerror(errno));
                //     continue;
                // }
                // while (fgets(result, sizeof(result), fp) != NULL) // 命令的执行结果通过文件指针fp返回，进行读取
                // {
                //     cmd_echo += result; // 执行结果，按行读取到cmd_echo里
                // }
                // fclose(fp);

                // v2:回显执行结果
                // sendto(_sock, cmd_echo.c_str(), cmd_echo.size(), 0, (struct sockaddr *)&peer, len);
            }
        }
    }

    ~UdpServer()
    {
        if (_sock >= 0)
        {
            close(_sock); // 关闭套接字
        }
    }

private:
    string _ip;     // ip地址
    uint16_t _port; // 端口号
    int _sock;      // 套接字

    unordered_map<string, struct sockaddr_in> user_list; // 实现网络聊天室
    vector<string> message_list;                         // 将所有聊天记录报存在该队列内
};
